#include "pcap.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/stat.h"
#include "sys/types.h"

#include "arpa/inet.h"
#include "linux/if_ether.h"
#include "linux/ip.h"
#include "netinet/tcp.h"
#include "linux/udp.h"
#include "common.h"

#include "ufile.h"
#include "unet.h"

using namespace std;

inline void save_packet(const void *data, int len, const char *fname)
{
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_lock(&lock);
    static int fd = -1;
    if (fd < 0) {
        char path[512] = {0};
        snprintf(path, sizeof(path), "%s_%lu.pcap", fname, time(NULL));
        fd = open(path, O_WRONLY | O_CREAT, S_IRWXU);
        if (fd < 0) {
            debug("open %s fail [%m]\n", path);
            pthread_mutex_unlock(&lock);
            return;
        }
        struct my_pcap_filehdr {
            uint32_t magic;
            uint16_t minor_version;
            uint16_t magot_version;
            uint32_t timezone;
            uint32_t sigflags;
            uint32_t snaplen;
            uint32_t linktype;
        };
        struct my_pcap_filehdr header = {
            0xa1b2c3d4,
            0x0002,
            0x0004,
            0x00000000,
            0x00000000,
            0x00040000,
            0x00000001,
        };
        if (write(fd, &header, sizeof(header)) != sizeof(header)) {
            debug("fail to write [%m]\n");
        }
    }
    struct my_pcap_pkthdr {
        uint32_t sec;
        uint32_t usec;
        uint32_t caplen;    /* length of portion present */
        uint32_t len;       /* length this packet (off wire) */
    } pcap_frame_head;
    struct timeval  tv; 
    gettimeofday(&tv, NULL);
    pcap_frame_head.sec = (uint32_t)tv.tv_sec;
    pcap_frame_head.usec = (uint32_t)tv.tv_usec;    
    pcap_frame_head.caplen = len;
    pcap_frame_head.len = len;
    if (write(fd, &pcap_frame_head, sizeof(pcap_frame_head)) != sizeof(pcap_frame_head)) {
        debug("fail to write [%m]\n");
    }
    if (write(fd, data, len) != len) {
        debug("fail to write [%m]\n");
    }
    fsync(fd);
    pthread_mutex_unlock(&lock);
}

/* chechsum */
uint16_t checksum(uint16_t *data, int num)
{
	uint32_t sum = 0;
	uint16_t check = 0;
	int index = 0;
	for(; index < num; index++)
	{
		sum += data[index];
		check = sum;
		if(sum & 0x10000)
			check++;
		sum = 0x01111;
	}
	return ~check;
}

/* get net packet header*/
/* ntohl may need to be called when use*/
int get_header(void *pkt, int len, struct hdr *head)
{
	uint8_t *data = (uint8_t*)pkt;
	
	head->ethhd = new ethhdr;
	// only ipv4 here
	head->iphd = new iphdr;

	int hlen = 0;	// header length
	/* skip broadcast packet*/
	if (unlikely(0 == memcmp(data, "\xff\xff\xff\xff\xff\xff", 6))) 
	{
		return 1;
	}
	/* ETH_II*/
	//host len
	//ARP 0x0806
	//IPV4 0x0800
	head->ethhd = (struct ethhdr*)data;
	if(head->ethhd->h_proto != 0x08)
		return 1;
	data += 14;
	len -= 14;
	
	/* ipv4*/
	head->iphd = (struct iphdr*)data;
	hlen = head->iphd->ihl * 4;
	data += hlen;
	len -= hlen;
	
	/* tcp or udp*/
	switch(head->iphd->protocol)
	{
		case 6:		//tcp
			head->tuhd.utcphd = new utcphdr;
			head->tuhd.utcphd->tcphd = (struct tcphdr*)data;
			hlen = head->tuhd.utcphd->tcphd->doff * 4;
			if(hlen > 20)	//sizeof(struct tcphdr) = 20
			{	//tcp option
				head->tuhd.utcphd->option = (struct toption*)data;
			}
			data += hlen;
			len -= len;
			break;
		case 17:	//udp
			head->tuhd.udphd = new udphdr;
			head->tuhd.udphd = (struct udphdr*)data;
			data += 8;
			len -= 8;
			break;
		default:
			//icmp	1
			//
			break;
	}
	
	/* return */
	return 0;
}

void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
	int * id = (int *)arg;

	printf("id: %d\n", ++(*id));
	printf("Packet length: %d\n", pkthdr->len);
	printf("Number of bytes: %d\n", pkthdr->caplen);
	printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));

	unsigned int i;
	for(i=0; i<pkthdr->len; ++i)
	{
		printf(" %02x ", packet[i]);
		if( (i + 1) % 16 == 0 )
		{
			printf("\n");
		}
	}
	save_packet(packet, pkthdr->caplen, "./tcp_fin.bin");
	printf("\n\n");
	if(*(id)>3)
		exit(0);
}

/* read local binary file and send them*/
// show in stdout when flag is set
// fd file decripter
int restream(const char *fname, int fd, int flag)
{
	const int size = 1;
	int count = 0, ret;
	unsigned char buf[64];
	FILE *fp = fopen(fname, "rb");
	if(NULL == fp){
		cout<<"open file "<<fname<<" failed"<<endl;
		return 0;
	}

	while(1){
		ret = fread(buf, size, 1, fp);
		count += size;

		hexdump(buf, ret, 0);
		if(feof(fp))
			return count;
	}

	return count;
}